#ifndef _SIGNAL_H
#define _SIGNAL_H

#include <sys/types.h>					// 类型头文件。定义了基本的系统数据类型

typedef int sig_atomic_t;				// 定义信号原子操作类型
typedef unsigned int sigset_t;			// 定义信号集类型

#define _NSIG             32			// 定义信号种类 -- 32 种
#define NSIG		_NSIG				// NSIG = _NSIG

// 以下这些是Linux 0.12内核中定义的信号。其中包括了POSIX.1要求的所有20个信号

// (Hangup) 当你不再有控制终端时内核会产生该信号，或者当你关闭
// Xterm 或断开 modem。由于后台程序没有控制的终端，因而它们常
// 用 SIGHUP 来发出需要重新读取其配置文件的信号。
#define SIGHUP		 1 // (Abort) 挂断控制终端或进程

// (Interrupt) 来自键盘的中断。通常终端驱动程序会将其与^C 绑定。
#define SIGINT		 2 // (Abort) 终止程序

// (Quit) 来自键盘的退出中断。通常终端驱动程序会将其与^\绑定。
#define SIGQUIT		 3 // (Dump) 程序被终止并产生 dump core 文件

// (Illegal Instruction) 程序出错或者执行了一条非法操作指令。
#define SIGILL		 4 // (Dump) 程序被终止并产生 dump core 文件

// (Breakpoint/Trace Trap) 调试用，跟踪断点。
#define SIGTRAP		 5

// (Abort) 放弃执行，异常结束。
#define SIGABRT		 6 // (Dump) 程序被终止并产生 dump core 文件

// (IO Trap) 同 SIGABRT
#define SIGIOT		 6 // (Dump) 程序被终止并产生 dump core 文件

//  (Unused) 没有使用。
#define SIGUNUSED	 7 

// (Floating Point Exception) 浮点异常。
#define SIGFPE		 8 // (Dump) 程序被终止并产生 dump core 文件

// (Kill) 程序被终止。该信号不能被捕获或者被忽略。想立刻终止一
// 个进程，就发送信号 9。注意程序将没有任何机会做清理工作。
#define SIGKILL		 9 // (Abort) 程序被终止

// (User defined Signal 1) 用户定义的信号。
#define SIGUSR1		10 // (Abort) 进程被终止

// (Segmentation Violation) 当程序引用无效的内存时会产生此信号。
// 比如：寻址没有映射的内存；寻址未许可的内存。
#define SIGSEGV		11 // (Dump) 程序被终止并产生 dump core 文件

// (User defined Signal 2) 保留给用户程序用于 IPC 或其他目的。
#define SIGUSR2		12 // (Abort) 进程被终止

// (Pipe) 当程序向一个套接字或管道写时由于没有读者而产生该信号。
#define SIGPIPE		13 // (Abort) 进程被终止

// (Alarm) 该信号会在用户调用 alarm 系统调用所设置的延迟时间到
// 后产生。该信号常用于判别系统调用超时。
#define SIGALRM		14 // (Abort) 进程被终止

// (Terminate) 用于和善地要求一个程序终止。它是 kill 的默认信号。
// 与 SIGKILL 不同，该信号能被捕获，这样就能在退出运行前做清理工作。
#define SIGTERM		15 // (Abort) 进程被终止

// (Stack fault on coprocessor) 协处理器堆栈错误。
#define SIGSTKFLT	16 // (Abort) 进程被终止

// (Child) 子进程发出。子进程已停止或终止。可改变其含义挪作它用。
#define SIGCHLD		17 // (Ignore) 子进程停止或结束

// (Continue) 该信号致使被 SIGSTOP 停止的进程恢复运行。可以被捕获。
#define SIGCONT		18 // (Continue) 恢复进程的执行

// (Stop) 停止进程的运行。该信号不可被捕获或忽略。
#define SIGSTOP		19 // (Stop) 停止进程运行

// (Terminal Stop) 向终端发送停止键序列。该信号可以被捕获或忽略。
#define SIGTSTP		20 // (Stop) 停止进程运行

// (TTY Input on Background) 后台进程试图从一个不再被控制的终端
// 上读取数据，此时该进程将被停止，直到收到 SIGCONT 信号。该
// 信号可以被捕获或忽略。
#define SIGTTIN		21 // (Stop) 停止进程运行

// (TTY Output on Background) 后台进程试图向一个不再被控制的终
// 端上输出数据，此时该进程将被停止，直到收到 SIGCONT 信号。
// 该信号可被捕获或忽略。
#define SIGTTOU		22 // (Stop) 停止进程运行

/* Ok, I haven't implemented sigactions, but trying to keep headers POSIX */
/* OK，我还没有实现sigactions的编制，但在头文件中仍希望遵守POSIX标准 */
// 上面原注释已经过时，因为在0.12内核中已经实现了sigaction()
// 下面是 sigaction 结构 sa_flags 标志字段可取的符号常数值。
#define SA_NOCLDSTOP	1				// 当子进程处于停止状态，就不对 SIGCHLD 处理	
#define SA_INTERRUPT	0x20000000		// 系统调用被信号中断后不重新启动系统调用
#define SA_NOMASK		0x40000000		// 不阻止在指定的信号处理程序中再收到该信号
#define SA_ONESHOT		0x80000000		// 信号句柄一旦被调用过就恢复到默认处理句柄

// 以下常量用于sigprocmask(how, )-- 向阻塞信号集添加/删除给定的信号，改变阻塞信号集
// (屏蔽码)。用于改变该函数的行为。
#define SIG_BLOCK          0			// 在阻塞信号集中加上给定信号
#define SIG_UNBLOCK        1			// 从阻塞信号集中删除指定信号
#define SIG_SETMASK        2			// 设置阻塞信号集

// 以下里三个常数符号都表示指向无返回值的函数指针，且都有一个int整型参数。这三个指针值
// 从逻辑上讲实际是不可能出现的函数地址值。 可作为signal()函数的第二个参数，用于告知内
// 核，让内核处理信号、忽略对信号的处理或信号处理返回错误。
#define SIG_DFL		((void (*)(int))0)	// 默认信号处理程序（信号句柄）
#define SIG_IGN		((void (*)(int))1)	// 忽略信号的处理程序
#define SIG_ERR		((void (*)(int))-1)	// 信号处理返回错误

// 下面定义初始操作设置sigaction结构信号屏蔽码的宏
#ifdef notdef
#define sigemptyset(mask) ((*(mask) = 0), 1)	// 将mask清零
#define sigfillset(mask) ((*(mask) = ~0), 1)	// 将mask所有比特位置位
#endif

// 下面是 sigaction 的数据结构。
// sa_handler 是对应某信号指定要采取的行动。可以用上面的 SIG_DFL ，或 SIG_IGN 来忽略该
// 信号，也可以是指向处理该信号函数的一个指针。
// sa_mask 给出了对信号的屏蔽码，在信号程序执行时将阻塞对这些信号的处理。
// sa_flags 指定改变信号处理过程的信号集。它是由97-100行的位标志定义的。
// sa_restorer 是恢复函数指针，由函数库Libc提供，用于清理用户态堆栈。
// 另外，引起触发信号处理的信号也将被阻塞，除非使用了 SA_NOMASK 标志。
struct sigaction {
	void (*sa_handler)(int);	// 信号处理句柄
	sigset_t sa_mask;			// 信号的屏蔽码，可以阻塞指定的信号集
	int sa_flags;				// 信号选项标志
	void (*sa_restorer)(void);	// 信号恢复函数指针（系统内部使用）在编译链接程序时由 Libc 函数库提供
};

// 下面 signal()函数用于为信号 _sig 安装一新的信号处理程序（信号句柄），与 sigaction()类
// 似。该函数含有两个参数：一个是指定需要捕获的信号 _sig；另一个是具有一参数且无返回值的
// 函数指针 _func。该函数返回值也是具有一个 int 参数（最后一个(int)）且无返回值的函数指针，
// 它是处理该信号的原处理句柄。
void (*signal(int _sig, void (*_func)(int)))(int);

// 下面两函数用于发送信号。kill() 用于向任何进程或进程组发送信号。raise()用于向当前进
// 程自身发送信号。其作用等价于 kill(getpid(),sig)。
int raise(int sig);
int kill(pid_t pid, int sig);

// 在进程的任务结构中，除有一个表示当前进程待处理信号的32位信号字段 signal 以外，还有一
// 个同样用32位表示的阻塞信号集（屏蔽信号集）字段 blocked ，其每个比特代表一个阻塞信号。
// 修改进程的屏蔽信号集可以阻塞或解除阻塞所指定的信号。 以下五个函数就是用于操作进程屏
// 蔽信号集。虽然实现起来很简单，但本版本内核中还未实现。
// 函数 sigaddset() 和 sigdelset() 用于对信号集中的信号进行增、删修改。 sigaddset()用
// 于向 mask 指向的信号集中增加指定的信号 signo ， sigdelset 则反之。函数 sigemptyset()和
// sigfillset() 用于初始化进程屏蔽信号集。在使用信号集前，每个程序都需要使用这两个函
// 数之一对屏蔽信号集进行初始化。 sigemptyset()用于清空屏蔽的所有信号，也即响应所有的
// 信号。 sigfillset()向信号集中置入所有信号，也即屏蔽所有信号。当然 SIGINT 和 SIGSTOP
// 是不能被屏蔽的。
// sigismember()用于测试一个指定信号是否在信号集中（1 - 是，0 - 不是，-1 - 出错）。
int sigaddset(sigset_t *mask, int signo);
int sigdelset(sigset_t *mask, int signo);
int sigemptyset(sigset_t *mask);
int sigfillset(sigset_t *mask);
int sigismember(sigset_t *mask, int signo); /* 1 - is, 0 - not, -1 error */

// 对set中的信号进行检测，看是否有挂起的信号。在set中返回进程中当前被阻塞的信号集
int sigpending(sigset_t *set);

// 下面函数用于改变进程目前被阻塞的信号集。若 oldset 不是 NULL ，则通过其返回进程当前屏蔽
// 信号集。若 set 指针不是 NULL ，则根据参数 how 指示修改进程屏蔽信号集。
int sigprocmask(int how, sigset_t *set, sigset_t *oldset);

// 下面函数用 sigmask 临时替换进程的信号屏蔽码,然后暂停该进程直到收到一信号。若捕捉到某
// 一信号并从该信号处理程序中返回，则该函数也返回，并且信号屏蔽码会恢复到调用调用前的值。
int sigsuspend(sigset_t *sigmask);

// sigaction() 函数用于改变进程在收到指定信号时所采取的行动，即改变信号的处理句柄
int sigaction(int sig, struct sigaction *act, struct sigaction *oldact);

#endif /* _SIGNAL_H */
